<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="FlLaX" id="FlLaX"><span data-lake-id="ufbd6fc3e" id="ufbd6fc3e">典型回答</span></h1>
  <p data-lake-id="uc70e7d4c" id="uc70e7d4c"><br></p>
  <p data-lake-id="ub77df4fd" id="ub77df4fd"><span data-lake-id="u27eb34ca" id="u27eb34ca">之所以不建议使用join查询，最主要的原因就是join的效率比较低。</span></p>
  <p data-lake-id="ud5b252c8" id="ud5b252c8"><span data-lake-id="u8128c5e9" id="u8128c5e9">​</span><br></p>
  <p data-lake-id="u512b7843" id="u512b7843"><span data-lake-id="u28ecc353" id="u28ecc353">MySQL是使用了嵌套循环（Nested-Loop Join）的方式来实现关联查询的，简单点说就是要通过两层循环，用第一张表做外循环，第二张表做内循环，外循环的每一条记录跟内循环中的记录作比较，符合条件的就输出。</span></p>
  <p data-lake-id="ue8044f87" id="ue8044f87"><span data-lake-id="ua0a6bc3d" id="ua0a6bc3d">​</span><br></p>
  <p data-lake-id="u4fc991e0" id="u4fc991e0"><span data-lake-id="ufa8af885" id="ufa8af885">而具体到算法实现上主要有simple nested loop，block nested loop和index nested loop这三种。而且这三种的效率都没有特别高。</span></p>
  <p data-lake-id="u99f4c391" id="u99f4c391"><span data-lake-id="u6c189d09" id="u6c189d09">​</span><br></p>
  <p data-lake-id="u3c99629a" id="u3c99629a"><span data-lake-id="u51fc8abd" id="u51fc8abd">MySQL是使用了嵌套循环（Nested-Loop Join）的方式来实现关联查询的，如果有2张表join的话，复杂度最高是O(n^2)，3张表则是O(n^3)...随着表越多，表中的数据量越多，JOIN的效率会呈指数级下降。</span></p>
  <p data-lake-id="ubfa01812" id="ubfa01812"><span data-lake-id="uc9c1ab7b" id="uc9c1ab7b">​</span><br></p>
  <p data-lake-id="u5817310c" id="u5817310c"><span data-lake-id="ud9328c38" id="ud9328c38">PS：MySQL 8.0中新增了 hash join算法：</span></p>
  <p data-lake-id="ued85f9a8" id="ued85f9a8"><span data-lake-id="u42475fdd" id="u42475fdd">​</span><br></p>
  <p data-lake-id="u98730c0a" id="u98730c0a"><span data-lake-id="ud60c8054" id="ud60c8054">​</span><br></p>
  <h1 data-lake-id="JZcGw" id="JZcGw"><span data-lake-id="u9be0d0bb" id="u9be0d0bb">扩展知识</span></h1>
  <p data-lake-id="u202eb83a" id="u202eb83a"><br></p>
  <h2 data-lake-id="ATesT" id="ATesT"><span data-lake-id="uddc3e996" id="uddc3e996">join</span></h2>
  <p data-lake-id="u4469af81" id="u4469af81"><br></p>
  <p data-lake-id="u9956c52d" id="u9956c52d"><span data-lake-id="u59a35b37" id="u59a35b37">在MySQL 中，可以使用 JOIN 在两个或多个表中进行联合查询，join有三种，分别是inner join、left join 和 right join。</span></p>
  <p data-lake-id="ubc8e8179" id="ubc8e8179"><span data-lake-id="u4a42f6cb" id="u4a42f6cb">​</span><br></p>
  <ul list="u8049b5fb">
   <li fid="ud301723e" data-lake-id="u032dfd76" id="u032dfd76"><span data-lake-id="u362adffa" id="u362adffa">INNER JOIN（内连接,或等值连接）：获取两个表中字段匹配关系的记录。</span></li>
  </ul>
  <ul list="u8049b5fb" data-lake-indent="1">
   <li fid="u55ab5ab9" data-lake-id="uf89526d0" id="uf89526d0"><span data-lake-id="u86f2ce2c" id="u86f2ce2c">取两个表的交集部分</span></li>
  </ul>
  <ul list="u8049b5fb" start="2">
   <li fid="ud301723e" data-lake-id="u84d82d37" id="u84d82d37"><span data-lake-id="ucaa0e5d7" id="ucaa0e5d7">LEFT JOIN（左连接）：获取左表所有记录，即使右表没有对应匹配的记录。</span></li>
  </ul>
  <ul list="u8049b5fb" data-lake-indent="1">
   <li fid="u5abe77cc" data-lake-id="udca1dd2e" id="udca1dd2e"><span data-lake-id="u4d3f17b3" id="u4d3f17b3">取两个表的交集部分+左表中的数据</span></li>
  </ul>
  <ul list="u8049b5fb" start="3">
   <li fid="ud301723e" data-lake-id="u00c66ced" id="u00c66ced"><span data-lake-id="u08e1a350" id="u08e1a350">RIGHT JOIN（右连接）： 与 LEFT JOIN 相反，用于获取右表所有记录，即使左表没有对应匹配的记录。</span></li>
  </ul>
  <ul list="u8049b5fb" data-lake-indent="1">
   <li fid="u1e36d27a" data-lake-id="u452055a8" id="u452055a8"><span data-lake-id="ua50afb46" id="ua50afb46">取两个表的交集部分+右表中的数据</span></li>
  </ul>
  <p data-lake-id="u8f5953d4" id="u8f5953d4"><br></p>
  <p data-lake-id="ubb26c8fb" id="ubb26c8fb"><span data-lake-id="ude7ad103" id="ude7ad103">在配合join一起使用的还有on关键字，用来指明关联查询的一些条件。</span></p>
  <h2 data-lake-id="E2t3l" id="E2t3l"><span data-lake-id="u783dfe38" id="u783dfe38">嵌套循环算法</span></h2>
  <p data-lake-id="u20395892" id="u20395892"><br></p>
  <p data-lake-id="u0369202c" id="u0369202c"><span data-lake-id="u3c587738" id="u3c587738">MySQL是使用了嵌套循环（Nested-Loop Join）的方式来实现关联查询的，具体到算法上面主要有simple nested loop join，block nested loop join和index nested loop join这三种。</span></p>
  <p data-lake-id="udc29d784" id="udc29d784"><span data-lake-id="u0dd75a40" id="u0dd75a40">​</span><br></p>
  <p data-lake-id="ucde2fc09" id="ucde2fc09"><span data-lake-id="u1c302ca0" id="u1c302ca0">而这三种的效率都没有特别高。</span></p>
  <p data-lake-id="ud53b41cf" id="ud53b41cf"><span data-lake-id="uf31f9a8f" id="uf31f9a8f">​</span><br></p>
  <ul list="ua49a1ea9">
   <li fid="ue66b38cb" data-lake-id="ud8e5785a" id="ud8e5785a"><span data-lake-id="uecc46ce0" id="uecc46ce0">simple nested loop，他的做法简单粗暴，就是全量扫描连接两张表进行数据的两两对比，所以他的复杂度可以认为是N*M</span></li>
  </ul>
  <blockquote data-lake-id="u1064b84e" id="u1064b84e">
   <p data-lake-id="uec2ec80c" id="uec2ec80c"><span data-lake-id="u8a4be494" id="u8a4be494">N是驱动表的数量，M是被驱动表的数量</span></p>
  </blockquote>
  <ul list="ua49a1ea9" start="2">
   <li fid="ue66b38cb" data-lake-id="ub56f0cb9" id="ub56f0cb9"><span data-lake-id="u6ea1a1a8" id="u6ea1a1a8">index nested loop，当Inner Loop的表用到字段有索引的话，可以用到索引进行查询数据，因为索引是B+树的，复杂度可以近似认为是N*logM。</span></li>
   <li fid="ue66b38cb" data-lake-id="u52acd8c3" id="u52acd8c3"><span data-lake-id="ud452fa71" id="ud452fa71">block nested loop，其实是引入了一个Buffer，会提前把外循环的一部分结果提前放到JOIN BUFFER中，然后内循环的每一行都和整个buffer的数据作比较。虽然比较次数还是N*M，但是因为join buffer是基于内存的，所以效率高很多。</span></li>
  </ul>
  <p data-lake-id="u3c8d04f9" id="u3c8d04f9"><br></p>
  <p data-lake-id="u5b51ebf4" id="u5b51ebf4"><span data-lake-id="u92fb5ed5" id="u92fb5ed5">所以，虽然MySQL已经尽可能的在优化了，但是这几种算法复杂度都还是挺高的，这也是为什么不建议在数据库中多表JOIN的原因。随着表越多，表中的数据量越多，JOIN的效率会呈指数级下降。</span></p>
  <h3 data-lake-id="R2JoQ" id="R2JoQ"><span data-lake-id="ub855b37b" id="ub855b37b" style="color: rgb(36, 41, 47)">不能用join如何做关联查询</span></h3>
  <p data-lake-id="u8751e5bd" id="u8751e5bd"><br></p>
  <p data-lake-id="u7663efa9" id="u7663efa9"><span data-lake-id="uc59700e3" id="uc59700e3" class="lake-fontsize-12" style="color: rgb(36, 41, 47)">如果不能通过数据库做关联查询，那么需要查询多表的数据的时候要怎么做呢？</span></p>
  <p data-lake-id="udd0fac48" id="udd0fac48"><span data-lake-id="u66156d60" id="u66156d60" class="lake-fontsize-12" style="color: rgb(36, 41, 47)">​</span><br></p>
  <p data-lake-id="u08e89a6d" id="u08e89a6d"><span data-lake-id="ufaa5eac0" id="ufaa5eac0" class="lake-fontsize-12" style="color: rgb(36, 41, 47)">主要有两种做法：</span></p>
  <p data-lake-id="u64194474" id="u64194474"><span data-lake-id="ub0c69550" id="ub0c69550" class="lake-fontsize-12" style="color: rgb(36, 41, 47)">​</span><br></p>
  <p data-lake-id="u193e04b3" id="u193e04b3"><span data-lake-id="uf6b7d5a3" id="uf6b7d5a3" class="lake-fontsize-12" style="color: rgb(36, 41, 47)">1、在内存中自己做关联，即先从数据库中把数据查出来之后，我们在代码中再进行二次查询，然后再进行关联。</span></p>
  <p data-lake-id="ud5adefaa" id="ud5adefaa"><span data-lake-id="u6a3fbbc6" id="u6a3fbbc6" class="lake-fontsize-12" style="color: rgb(36, 41, 47)">​</span><br></p>
  <p data-lake-id="ud0c4e393" id="ud0c4e393"><span data-lake-id="u8e21b616" id="u8e21b616" class="lake-fontsize-12" style="color: rgb(36, 41, 47)">2、数据冗余，那就是把一些重要的数据在表中做冗余，这样就可以避免关联查询了。</span></p>
  <p data-lake-id="u3f6f8575" id="u3f6f8575"><span data-lake-id="uec863121" id="uec863121" class="lake-fontsize-12" style="color: rgb(36, 41, 47)">​</span><br></p>
  <p data-lake-id="uf1b35903" id="uf1b35903"><span data-lake-id="ub4ba9993" id="ub4ba9993" class="lake-fontsize-12" style="color: rgb(36, 41, 47)">3、宽表，就是基于一定的join关系，把数据库中多张表的数据打平做一张大宽表，可以同步到ES或者干脆直接在数据库中直接查都可以</span></p>
 </body>
</html>